function _get(val) {
    return K(val)[0];
}

function _getDoc(node) {
    if (!node) {
        return document;
    }
    return node.ownerDocument || node.document || node;
}

function _getWin(node) {
    if (!node) {
        return window;
    }
    var doc = _getDoc(node);
    return doc.parentWindow || doc.defaultView;
}

function _setHtml(el, html) {
    if (el.nodeType != 1) {
        return;
    }
    var doc = _getDoc(el);
    try {
        el.innerHTML = '<img id="__kindeditor_temp_tag__" width="0" height="0" style="display:none;" />' + html;
        var temp = doc.getElementById('__kindeditor_temp_tag__');
        temp.parentNode.removeChild(temp);
    } catch (e) {
        // bugfix: 在IE上innerHTML有时候报错
        K(el).empty();
        K('@' + html, doc).each(function () {
            el.appendChild(this);
        });
    }
}

function _hasClass(el, cls) {
    return _inString(cls, el.className, ' ');
}

function _setAttr(el, key, val) {
    if (_IE && _V < 8 && key.toLowerCase() == 'class') {
        key = 'className';
    }
    el.setAttribute(key, '' + val);
}

function _removeAttr(el, key) {
    if (_IE && _V < 8 && key.toLowerCase() == 'class') {
        key = 'className';
    }
    _setAttr(el, key, '');
    el.removeAttribute(key);
}

function _getNodeName(node) {
    if (!node || !node.nodeName) {
        return '';
    }
    return node.nodeName.toLowerCase();
}

function _computedCss(el, key) {
    var self = this, win = _getWin(el), camelKey = _toCamel(key), val = '';
    if (win.getComputedStyle) {
        var style = win.getComputedStyle(el, null);
        val = style[camelKey] || style.getPropertyValue(key) || el.style[camelKey];
    } else if (el.currentStyle) {
        val = el.currentStyle[camelKey] || el.style[camelKey];
    }
    return val;
}

function _hasVal(node) {
    return !!_VALUE_TAG_MAP[_getNodeName(node)];
}

function _docElement(doc) {
    doc = doc || document;
    return _QUIRKS ? doc.body : doc.documentElement;
}

function _docHeight(doc) {
    var el = _docElement(doc);
    return Math.max(el.scrollHeight, el.clientHeight);
}

function _docWidth(doc) {
    var el = _docElement(doc);
    return Math.max(el.scrollWidth, el.clientWidth);
}

function _getScrollPos(doc) {
    doc = doc || document;
    var x, y;
    if (_IE || _NEWIE || _OPERA) {
        x = _docElement(doc).scrollLeft;
        y = _docElement(doc).scrollTop;
    } else {
        x = _getWin(doc).scrollX;
        y = _getWin(doc).scrollY;
    }
    return {x: x, y: y};
}

// create KNode class
function KNode(node) {
    this.init(node);
}

_extend(KNode, {
    init: function (node) {
        var self = this;
        node = _isArray(node) ? node : [node];
        var length = 0;
        for (var i = 0, len = node.length; i < len; i++) {
            if (node[i]) {
                self[i] = node[i].constructor === KNode ? node[i][0] : node[i];
                length++;
            }
        }
        self.length = length;
        self.doc = _getDoc(self[0]);
        self.name = _getNodeName(self[0]);
        self.type = self.length > 0 ? self[0].nodeType : null;
        self.win = _getWin(self[0]);
    },
    each: function (fn) {
        var self = this;
        for (var i = 0; i < self.length; i++) {
            if (fn.call(self[i], i, self[i]) === false) {
                return self;
            }
        }
        return self;
    },
    bind: function (type, fn) {
        this.each(function () {
            _bind(this, type, fn);
        });
        return this;
    },
    unbind: function (type, fn) {
        this.each(function () {
            _unbind(this, type, fn);
        });
        return this;
    },
    fire: function (type) {
        if (this.length < 1) {
            return this;
        }
        _fire(this[0], type);
        return this;
    },
    hasAttr: function (key) {
        if (this.length < 1) {
            return false;
        }
        return !!_getAttr(this[0], key);
    },
    attr: function (key, val) {
        var self = this;
        if (key === undefined) {
            return _getAttrList(self.outer());
        }
        if (typeof key === 'object') {
            _each(key, function (k, v) {
                self.attr(k, v);
            });
            return self;
        }
        if (val === undefined) {
            val = self.length < 1 ? null : _getAttr(self[0], key);
            return val === null ? '' : val;
        }
        self.each(function () {
            _setAttr(this, key, val);
        });
        return self;
    },
    removeAttr: function (key) {
        this.each(function () {
            _removeAttr(this, key);
        });
        return this;
    },
    get: function (i) {
        if (this.length < 1) {
            return null;
        }
        return this[i || 0];
    },
    eq: function (i) {
        if (this.length < 1) {
            return null;
        }
        return this[i] ? new KNode(this[i]) : null;
    },
    hasClass: function (cls) {
        if (this.length < 1) {
            return false;
        }
        return _hasClass(this[0], cls);
    },
    addClass: function (cls) {
        this.each(function () {
            if (!_hasClass(this, cls)) {
                this.className = _trim(this.className + ' ' + cls);
            }
        });
        return this;
    },
    removeClass: function (cls) {
        this.each(function () {
            if (_hasClass(this, cls)) {
                this.className = _trim(this.className.replace(new RegExp('(^|\\s)' + cls + '(\\s|$)'), ' '));
            }
        });
        return this;
    },
    html: function (val) {
        var self = this;
        if (val === undefined) {
            if (self.length < 1 || self.type != 1) {
                return '';
            }
            return _formatHtml(self[0].innerHTML);
        }
        self.each(function () {
            _setHtml(this, val);
        });
        return self;
    },
    text: function () {
        var self = this;
        if (self.length < 1) {
            return '';
        }
        return _IE ? self[0].innerText : self[0].textContent;
    },
    hasVal: function () {
        if (this.length < 1) {
            return false;
        }
        return _hasVal(this[0]);
    },
    val: function (val) {
        var self = this;
        if (val === undefined) {
            if (self.length < 1) {
                return '';
            }
            return self.hasVal() ? self[0].value : self.attr('value');
        } else {
            self.each(function () {
                if (_hasVal(this)) {
                    this.value = val;
                } else {
                    _setAttr(this, 'value', val);
                }
            });
            return self;
        }
    },
    css: function (key, val) {
        var self = this;
        if (key === undefined) {
            return _getCssList(self.attr('style'));
        }
        if (typeof key === 'object') {
            _each(key, function (k, v) {
                self.css(k, v);
            });
            return self;
        }
        if (val === undefined) {
            if (self.length < 1) {
                return '';
            }
            return self[0].style[_toCamel(key)] || _computedCss(self[0], key) || '';
        }
        self.each(function () {
            this.style[_toCamel(key)] = val;
        });
        return self;
    },
    width: function (val) {
        var self = this;
        if (val === undefined) {
            if (self.length < 1) {
                return 0;
            }
            return self[0].offsetWidth;
        }
        return self.css('width', _addUnit(val));
    },
    height: function (val) {
        var self = this;
        if (val === undefined) {
            if (self.length < 1) {
                return 0;
            }
            return self[0].offsetHeight;
        }
        return self.css('height', _addUnit(val));
    },
    opacity: function (val) {
        this.each(function () {
            if (this.style.opacity === undefined) {
                this.style.filter = val == 1 ? '' : 'alpha(opacity=' + (val * 100) + ')';
            } else {
                this.style.opacity = val == 1 ? '' : val;
            }
        });
        return this;
    },
    data: function (key, val) {
        var self = this;
        key = 'kindeditor_data_' + key;
        if (val === undefined) {
            if (self.length < 1) {
                return null;
            }
            return self[0][key];
        }
        this.each(function () {
            this[key] = val;
        });
        return self;
    },
    pos: function () {
        var self = this, node = self[0], x = 0, y = 0;
        if (node) {
            if (node.getBoundingClientRect) {
                var box = node.getBoundingClientRect(),
                    pos = _getScrollPos(self.doc);
                x = box.left + pos.x;
                y = box.top + pos.y;
            } else {
                while (node) {
                    x += node.offsetLeft;
                    y += node.offsetTop;
                    node = node.offsetParent;
                }
            }
        }
        return {x: _round(x), y: _round(y)};
    },
    clone: function (bool) {
        if (this.length < 1) {
            return new KNode([]);
        }
        return new KNode(this[0].cloneNode(bool));
    },
    append: function (expr) {
        this.each(function () {
            if (this.appendChild) {
                this.appendChild(_get(expr));
            }
        });
        return this;
    },
    appendTo: function (expr) {
        this.each(function () {
            _get(expr).appendChild(this);
        });
        return this;
    },
    before: function (expr) {
        this.each(function () {
            this.parentNode.insertBefore(_get(expr), this);
        });
        return this;
    },
    after: function (expr) {
        this.each(function () {
            if (this.nextSibling) {
                this.parentNode.insertBefore(_get(expr), this.nextSibling);
            } else {
                this.parentNode.appendChild(_get(expr));
            }
        });
        return this;
    },
    replaceWith: function (expr) {
        var nodes = [];
        this.each(function (i, node) {
            _unbind(node);
            var newNode = _get(expr);
            node.parentNode.replaceChild(newNode, node);
            nodes.push(newNode);
        });
        return K(nodes);
    },
    empty: function () {
        var self = this;
        self.each(function (i, node) {
            var child = node.firstChild;
            while (child) {
                if (!node.parentNode) {
                    return;
                }
                var next = child.nextSibling;
                child.parentNode.removeChild(child);
                child = next;
            }
        });
        return self;
    },
    remove: function (keepChilds) {
        var self = this;
        self.each(function (i, node) {
            if (!node.parentNode) {
                return;
            }
            _unbind(node);
            if (keepChilds) {
                var child = node.firstChild;
                while (child) {
                    var next = child.nextSibling;
                    node.parentNode.insertBefore(child, node);
                    child = next;
                }
            }
            node.parentNode.removeChild(node);
            delete self[i];
        });
        self.length = 0;
        return self;
    },
    show: function (val) {
        var self = this;
        if (val === undefined) {
            val = self._originDisplay || '';
        }
        if (self.css('display') != 'none') {
            return self;
        }
        return self.css('display', val);
    },
    hide: function () {
        var self = this;
        if (self.length < 1) {
            return self;
        }
        self._originDisplay = self[0].style.display;
        return self.css('display', 'none');
    },
    outer: function () {
        var self = this;
        if (self.length < 1) {
            return '';
        }
        var div = self.doc.createElement('div'), html;
        div.appendChild(self[0].cloneNode(true));
        html = _formatHtml(div.innerHTML);
        div = null;
        return html;
    },
    isSingle: function () {
        return !!_SINGLE_TAG_MAP[this.name];
    },
    isInline: function () {
        return !!_INLINE_TAG_MAP[this.name];
    },
    isBlock: function () {
        return !!_BLOCK_TAG_MAP[this.name];
    },
    isStyle: function () {
        return !!_STYLE_TAG_MAP[this.name];
    },
    isControl: function () {
        return !!_CONTROL_TAG_MAP[this.name];
    },
    contains: function (otherNode) {
        if (this.length < 1) {
            return false;
        }
        return _contains(this[0], _get(otherNode));
    },
    parent: function () {
        if (this.length < 1) {
            return null;
        }
        var node = this[0].parentNode;
        return node ? new KNode(node) : null;
    },
    children: function () {
        if (this.length < 1) {
            return new KNode([]);
        }
        var list = [], child = this[0].firstChild;
        while (child) {
            if (child.nodeType != 3 || _trim(child.nodeValue) !== '') {
                list.push(child);
            }
            child = child.nextSibling;
        }
        return new KNode(list);
    },
    first: function () {
        var list = this.children();
        return list.length > 0 ? list.eq(0) : null;
    },
    last: function () {
        var list = this.children();
        return list.length > 0 ? list.eq(list.length - 1) : null;
    },
    index: function () {
        if (this.length < 1) {
            return -1;
        }
        var i = -1, sibling = this[0];
        while (sibling) {
            i++;
            sibling = sibling.previousSibling;
        }
        return i;
    },
    prev: function () {
        if (this.length < 1) {
            return null;
        }
        var node = this[0].previousSibling;
        return node ? new KNode(node) : null;
    },
    next: function () {
        if (this.length < 1) {
            return null;
        }
        var node = this[0].nextSibling;
        return node ? new KNode(node) : null;
    },
    scan: function (fn, order) {
        if (this.length < 1) {
            return;
        }
        order = (order === undefined) ? true : order;

        function walk(node) {
            var n = order ? node.firstChild : node.lastChild;
            while (n) {
                var next = order ? n.nextSibling : n.previousSibling;
                if (fn(n) === false) {
                    return false;
                }
                if (walk(n) === false) {
                    return false;
                }
                n = next;
            }
        }

        walk(this[0]);
        return this;
    }
});

_each(('blur,focus,focusin,focusout,load,resize,scroll,unload,click,dblclick,' +
    'mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,' +
    'change,select,submit,keydown,keypress,keyup,error,contextmenu').split(','), function (i, type) {
    KNode.prototype[type] = function (fn) {
        return fn ? this.bind(type, fn) : this.fire(type);
    };
});

var _K = K;

K = function (expr, root) {
    if (expr === undefined || expr === null) {
        return;
    }

    function newNode(node) {
        if (!node[0]) {
            node = [];
        }
        return new KNode(node);
    }

    if (typeof expr === 'string') {
        if (root) {
            root = _get(root);
        }
        var length = expr.length;
        if (expr.charAt(0) === '@') {
            expr = expr.substr(1);
        }
        // HTML
        // 包含HTML代码，或者第一个字符为"@"时当做HTML字符串
        if (expr.length !== length || /<.+>/.test(expr)) {
            var doc = root ? root.ownerDocument || root : document,
                div = doc.createElement('div'), list = [];
            // HTML前添加IMG，防止IE抛出异常
            div.innerHTML = '<img id="__kindeditor_temp_tag__" width="0" height="0" style="display:none;" />' + expr;
            for (var i = 0, len = div.childNodes.length; i < len; i++) {
                var child = div.childNodes[i];
                if (child.id == '__kindeditor_temp_tag__') {
                    continue;
                }
                list.push(child);
            }
            return newNode(list);
        }
        // selector
        return newNode(_queryAll(expr, root));
    }
    // KNode
    if (expr && expr.constructor === KNode) {
        return expr;
    }
    // jQuery object
    if (expr.toArray) {
        expr = expr.toArray();
    }
    // Native NodeList
    if (_isArray(expr)) {
        return newNode(expr);
    }
    // Native Node
    return newNode(_toArray(arguments));
};

_each(_K, function (key, val) {
    K[key] = val;
});

K.NodeClass = KNode;

window.KindEditor = K;
